package com.dhq.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;

public class DESFileEncryption {
    //密钥
    public final static String sKey = "AD67EA2F3BE6E5ADD368DFE03120BXZ8PCCfPDL40GJFQ4GL";

    private static DESFileEncryption desFileEncryption = new DESFileEncryption();

    private DESFileEncryption(){}

    public static DESFileEncryption getInstance(){
        return desFileEncryption;
    }
    
    /**
     * 文件加密
     *
     * @param sourcePath 原始文件路径
     * @param targetPath 加密后的存储路径
     */
    public void encryptFile(String sourcePath, String targetPath) {
        try {
            CipherInputStream cipherInputStream = getCipherInputStream(new FileInputStream(sourcePath));
            FileUtil.writeFromStream(cipherInputStream, targetPath);
            IoUtil.close(cipherInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解密文件
     * @param sourcePath 待解密的文件路径
     * @param targetPath 解密后的文件路径
     */
    public void decryptFile(String sourcePath, String targetPath) {
        try {
            byte[] bytes = IoUtil.readBytes(new FileInputStream(sourcePath));
            ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(bytes);

            byte[] bytK1 = getKeyByStr(sKey);
            Cipher cipher = getCipher(bytK1, Cipher.DECRYPT_MODE);


            OutputStream out = new FileOutputStream(targetPath);
            CipherOutputStream cipherOutputStream = new CipherOutputStream(out, cipher);
            IoUtil.copy(byteArrayInputStream, cipherOutputStream);

            IoUtil.close(byteArrayInputStream);
            IoUtil.close(out);
            IoUtil.close(cipherOutputStream);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }

    }

    /**
     * 文件加密流
     * @param bytes 字节数组
     * @return javax.crypto.CipherInputStream
     * @throws InvalidKeySpecException 加密
     * @throws NoSuchPaddingException 解密报错
     * @throws NoSuchAlgorithmException 加密报错
     * @throws InvalidKeyException 验证Key异常
     */
    public CipherInputStream getCipherInputStream(byte[] bytes) throws InvalidKeySpecException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
        ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(bytes);
        CipherInputStream cipherInputStream = this.getCipherInputStream(byteArrayInputStream);
        return cipherInputStream;
    }

    /**
     * 文件加密流
     * @param inputStream 文件输入流
     * @return javax.crypto.CipherInputStream
     * @throws InvalidKeySpecException 加密
     * @throws NoSuchPaddingException 解密报错
     * @throws NoSuchAlgorithmException 加密报错
     * @throws InvalidKeyException 验证Key异常
     */
    public CipherInputStream getCipherInputStream(InputStream inputStream) throws InvalidKeySpecException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
        byte[] bytK1 = getKeyByStr(sKey);

        Cipher cipher = getCipher(bytK1, Cipher.ENCRYPT_MODE);
        CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);

        IoUtil.close(inputStream);
        return cipherInputStream;
    }

    /**
     * 获取文件的解密输入流
     * @param sourcePath 原加密文件路径
     * @return java.io.ByteArrayInputStream
     */
    public InputStream getDecodeInputStream(String sourcePath) {
        try {
            String targetPath = getTempPath(sourcePath);
            //解密
            decryptFile(sourcePath, targetPath);
            File targetFile = new File(targetPath);
            FileInputStream fileInputStream = new FileInputStream(targetFile);
            byte[] readBytes = IoUtil.readBytes(fileInputStream);
            IoUtil.close(fileInputStream);
            //关闭后才能删除临时文件 fileInputStream
            delFile(targetPath);
            ByteArrayInputStream arrayInputStream = IoUtil.toStream(readBytes);
            return arrayInputStream;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 删除文件
     * @param targetPath 需要删除的文件路径
     */
    private void delFile(String targetPath){
        File targetFile = new File(targetPath);
        if (targetFile.exists()) {
            boolean del = FileUtil.del(targetFile);
            System.out.println("删除临时文件：" + del);
        }
    }

    /**
     * 获取解密临时文件路径
     * @param path 加密文件路径
     * @return 解密临时文件路径
     */
    private String getTempPath(String path) {
        int lastIndexOf = path.lastIndexOf('.');
        String file_path = path.substring(0, lastIndexOf);
        //拼接解密后的临时存储文件
        String targetPath = file_path + UUID.randomUUID().toString().replaceAll("-","") + ".temp";
        return targetPath;
    }

    //region Des
    /**
     * 得到密码类
     *
     * @param bytKey
     * @param i      ENCRYPT_MODE = 1 DECRYPT_MODE = 2
     * @return
     * @throws InvalidKeyException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     */
    public Cipher getCipher(byte[] bytKey, int i) throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException {
        DESKeySpec desKS = new DESKeySpec(bytKey);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
        SecretKey sk = skf.generateSecret(desKS);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(i, sk);
        return cipher;
    }

    public static byte[] getKeyByStr(String str) {
        byte[] bRet = new byte[str.length() / 2];

        for (int i = 0; i < str.length() / 2; ++i) {
            Integer itg = new Integer(16 * getChrInt(str.charAt(2 * i)) + getChrInt(str.charAt(2 * i + 1)));
            bRet[i] = itg.byteValue();
        }

        return bRet;
    }

    public static int getChrInt(char chr) {
        int iRet = 0;
        if (chr == "0".charAt(0)) {
            iRet = 0;
        }

        if (chr == "1".charAt(0)) {
            iRet = 1;
        }

        if (chr == "2".charAt(0)) {
            iRet = 2;
        }

        if (chr == "3".charAt(0)) {
            iRet = 3;
        }

        if (chr == "4".charAt(0)) {
            iRet = 4;
        }

        if (chr == "5".charAt(0)) {
            iRet = 5;
        }

        if (chr == "6".charAt(0)) {
            iRet = 6;
        }

        if (chr == "7".charAt(0)) {
            iRet = 7;
        }

        if (chr == "8".charAt(0)) {
            iRet = 8;
        }

        if (chr == "9".charAt(0)) {
            iRet = 9;
        }

        if (chr == "A".charAt(0)) {
            iRet = 10;
        }

        if (chr == "B".charAt(0)) {
            iRet = 11;
        }

        if (chr == "C".charAt(0)) {
            iRet = 12;
        }

        if (chr == "D".charAt(0)) {
            iRet = 13;
        }

        if (chr == "E".charAt(0)) {
            iRet = 14;
        }

        if (chr == "F".charAt(0)) {
            iRet = 15;
        }

        return iRet;
    }
    //endregion

    public static void main(String[] args) {
        String sp = "E:\\文件加解密\\原始文件.docx";//原始文件
        String dp = "E:\\文件加解密\\加密文件.docx";//加密后文件
        String dp2 = "E:\\文件加解密\\解密文件.docx";//解密后文件

        DESFileEncryption fileEncrypter = new DESFileEncryption();
        //文件加密
        fileEncrypter.encryptFile(sp,dp);
        //文件解密
        fileEncrypter.decryptFile(dp,dp2);
    }
}
